package server

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"gmap-backend/internal/config"
	"gmap-backend/internal/ipc/namedpipe"
	"gmap-backend/internal/server/serviceimpl/clipboard"
	"gmap-backend/internal/server/serviceimpl/ctxmenu"
	"gmap-backend/internal/server/serviceimpl/encservice"
	"gmap-backend/internal/server/serviceimpl/fileservice"
	"gmap-backend/internal/server/serviceimpl/fulltextsearch"
	"gmap-backend/internal/server/serviceimpl/loadiconsvc"
	"gmap-backend/internal/server/serviceimpl/pathservice"
	"gmap-backend/internal/server/serviceimpl/runnerservice"
	"gmap-backend/internal/server/serviceimpl/scrshotcombine"
	"gmap-backend/internal/server/serviceimpl/zipsvc"
	"gmap-backend/internal/server/vo"
	"gmap-backend/internal/util/commonutil"
	"log"
	"os"
	"reflect"
)

type ClientUsedServInfo struct {
	Pid          int
	PipeFullName string
}

type Holder struct {
	AppConf *config.AppConfig
}

// MInvoke 表示处理器方法和请求参数类型
type MInvoke struct {
	method  reflect.Value
	reqType reflect.Type
}

// action type与对应处理器方法的对应关系
var handlerMap map[string]MInvoke = map[string]MInvoke{
	vo.ActionEnc: MInvoke{
		reflect.ValueOf(encservice.Encrypt),
		reflect.TypeOf(vo.TxtReq{}),
	},
	vo.ActionDec: MInvoke{
		reflect.ValueOf(encservice.Decrypt),
		reflect.TypeOf(vo.TxtReq{}),
	},
	vo.ActionDecBatch: MInvoke{
		reflect.ValueOf(encservice.DecryptBatch),
		reflect.TypeOf(vo.TxtsReq{}),
	},
	vo.ActionCmdRun: MInvoke{
		reflect.ValueOf(runnerservice.ExecShell),
		reflect.TypeOf(vo.CmdRunReq{}),
	},
	vo.ActionCmdStart: MInvoke{
		reflect.ValueOf(runnerservice.ExecShellStart),
		reflect.TypeOf(vo.CmdRunReq{}),
	},
	vo.ActionCmdOpen: MInvoke{
		reflect.ValueOf(runnerservice.OpenCmdWithWorkDir),
		reflect.TypeOf(vo.PathReq{}),
	},
	vo.ActionPathRecognize: MInvoke{
		reflect.ValueOf(pathservice.RecognizePath),
		reflect.TypeOf(vo.PathReq{}),
	},
	vo.ActionRunFileOrDir: MInvoke{
		reflect.ValueOf(fileservice.RunFileOrDir),
		reflect.TypeOf(vo.PathReq{}),
	},
	vo.ActionSelectFileOrDir: MInvoke{
		reflect.ValueOf(fileservice.SelectFileOrDir),
		reflect.TypeOf(vo.PathReq{}),
	},
	vo.ActionOpenAs: MInvoke{
		reflect.ValueOf(fileservice.OpenAs),
		reflect.TypeOf(vo.PathReq{}),
	},
	vo.ActionOpenIn: MInvoke{
		reflect.ValueOf(fileservice.OpenIn),
		reflect.TypeOf(vo.OpenInReq{}),
	},
	vo.ActionOpenBy: MInvoke{
		reflect.ValueOf(fileservice.OpenBy),
		reflect.TypeOf(vo.OpenByReq{}),
	},
	vo.ActionLoadIcon: MInvoke{
		reflect.ValueOf(loadiconsvc.LoadIcon),
		reflect.TypeOf(vo.PathReq{}),
	},
	vo.ActionGetUrlFromClip: MInvoke{
		reflect.ValueOf(clipboard.GetUrlFromClipboard),
		reflect.TypeOf(vo.BaseActionReq{}),
	},
	vo.ActionGetImgUrlFromClip: MInvoke{
		reflect.ValueOf(clipboard.GetImgUrlFromClipboard),
		reflect.TypeOf(vo.BaseActionReq{}),
	},
	vo.ActionSaveClipFileLocal: MInvoke{
		reflect.ValueOf(clipboard.SaveClipFileToLocal),
		reflect.TypeOf(vo.PathReq{}),
	},
	vo.ActionSaveClipImgLocal: MInvoke{
		reflect.ValueOf(clipboard.SaveClipImgToLocal),
		reflect.TypeOf(vo.PathReq{}),
	},
	vo.ActionSaveClipFileRemote: MInvoke{
		reflect.ValueOf(clipboard.SaveClipFileToRemote),
		reflect.TypeOf(vo.PathReq{}),
	},
	vo.ActionSaveClipImgRemote: MInvoke{
		reflect.ValueOf(clipboard.SaveClipImgToRemote),
		reflect.TypeOf(vo.PathReq{}),
	},
	vo.ActionGetClipPossibleOp: MInvoke{
		reflect.ValueOf(clipboard.GetClipPossibleOp),
		reflect.TypeOf(vo.BaseActionReq{}),
	},
	vo.ActionLoadCtxMenu: MInvoke{
		reflect.ValueOf(ctxmenu.LoadCtxMenu),
		reflect.TypeOf(vo.PathReq{}),
	},
	vo.ActionZipDir: MInvoke{
		reflect.ValueOf(zipsvc.ZipDir),
		reflect.TypeOf(vo.ZipDirReq{}),
	},
	vo.ActionFulltextSearchTags: MInvoke{
		reflect.ValueOf(fulltextsearch.SearchAllTags),
		reflect.TypeOf(vo.BaseActionReq{}),
	},
	vo.ActionFulltextSearch: MInvoke{
		reflect.ValueOf(fulltextsearch.Search),
		reflect.TypeOf(vo.TxtReq{}),
	},
	vo.ActionScrshotCombine: MInvoke{
		reflect.ValueOf(scrshotcombine.CombineImg),
		reflect.TypeOf(vo.CombineShotReq{}),
	},
}

// Start 启动服务：
// 注册各服务
// 生成服务启动信息文件
// 异步写启动完成标识
// 开始监听服务
func Start(appConfig *config.AppConfig) {
	servInfo := ClientUsedServInfo{
		Pid:          os.Getpid(),
		PipeFullName: appConfig.ServerConfig.PipeName,
	}
	servInfoBytes, err := json.Marshal(&servInfo)
	commonutil.ThrowIfNeed(err)
	serverInfoBase64 := base64.StdEncoding.EncodeToString(servInfoBytes)

	conf := namedpipe.ServerConf{
		ReqCompress:     false,
		RespCompress:    false,
		RespCompressLev: 0,
	}
	holder := Holder{AppConf: appConfig}
	readyCallback := func() {
		// 向控制台输出服务器信息：ReadySymbol + base64服务器信息 + ReadySymbol
		// 原来方式为：把服务器信息写入文件，再向控制台输出启动完成的标志。现在不借助文件，直接通过约定格式向控制台输出，再由客户端解析
		fmt.Printf("%s%s%s", appConfig.ServerConfig.ReadySymbol, serverInfoBase64, appConfig.ServerConfig.ReadySymbol)
	}
	namedpipe.StartNamedPipeServer(appConfig.ServerConfig.PipeName, &conf, readyCallback, &holder)
}

func (h *Holder) Handle(data []byte, bins [][]byte, writer namedpipe.Writer) {
	generalReq := vo.BaseActionReq{}
	err := json.Unmarshal(data, &generalReq)
	if nil != err {
		log.Println(err)
		return
	}

	// 异常处理
	defer func() {
		if e := recover(); nil != e {
			resp := vo.BaseActionResp{}
			resp.State = -1

			switch e.(type) {
			case string:
				str, _ := e.(string)
				resp.Msg = fmt.Sprintf("%s", str)
			case error:
				err, _ := e.(error)
				resp.Msg = fmt.Sprintf("%s", err.Error())
			default:
				resp.Msg = fmt.Sprintf("%v", e)
			}
			log.Println(resp.Msg)
			bs, _ := json.Marshal(&resp)
			writer.Write(bs)
		}
	}()

	// 反射调用对应的处理器方法
	// 如果不返回值，则自动生成一个BaseActionResp
	var respPtr any
	methodInvoker, ok := handlerMap[generalReq.Action]
	if !ok {
		panic(fmt.Sprintf("未知的动作类型：%v", generalReq.Action))
	}
	reqPtr := reflect.New(methodInvoker.reqType).Interface()
	json.Unmarshal(data, reqPtr)
	returnVals := methodInvoker.method.Call([]reflect.Value{
		reflect.ValueOf(h.AppConf),
		reflect.ValueOf(reqPtr),
		reflect.ValueOf(bins),
	})
	hasBinaryContent := false
	var binaryContent [][]byte
	if nil != returnVals && 0 < len(returnVals) {
		respPtr = returnVals[0].Interface()
		if nil == respPtr {
			respPtr = &vo.BaseActionResp{}
		}
		if len(returnVals) >= 2 {
			if bin, ok := returnVals[1].Interface().([][]byte); ok {
				hasBinaryContent = true
				binaryContent = bin
			}
		}
	} else {
		respPtr = &vo.BaseActionResp{}
	}

	// 向结果中填充请求id信息
	bs, e := json.Marshal(respPtr)
	commonutil.ThrowIfNeed(e)
	if !hasBinaryContent {
		writer.Write(bs)
		return
	}
	writer.WriteWithBins(bs, binaryContent)
}
